/*
 * ImageToolbox is an image editor for android
 * Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * You should have received a copy of the Apache License
 * along with this program.  If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
 */

package ru.tech.imageresizershrinker.core.filters.presentation.widget

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Done
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import ru.tech.imageresizershrinker.core.domain.model.ColorModel
import ru.tech.imageresizershrinker.core.domain.model.FileModel
import ru.tech.imageresizershrinker.core.domain.model.ImageModel
import ru.tech.imageresizershrinker.core.domain.utils.roundTo
import ru.tech.imageresizershrinker.core.filters.domain.model.BlurEdgeMode
import ru.tech.imageresizershrinker.core.filters.domain.model.ClaheParams
import ru.tech.imageresizershrinker.core.filters.domain.model.EnhancedZoomBlurParams
import ru.tech.imageresizershrinker.core.filters.domain.model.FadeSide
import ru.tech.imageresizershrinker.core.filters.domain.model.FilterValueWrapper
import ru.tech.imageresizershrinker.core.filters.domain.model.GlitchParams
import ru.tech.imageresizershrinker.core.filters.domain.model.LinearGaussianParams
import ru.tech.imageresizershrinker.core.filters.domain.model.LinearTiltShiftParams
import ru.tech.imageresizershrinker.core.filters.domain.model.PaletteTransferSpace
import ru.tech.imageresizershrinker.core.filters.domain.model.PopArtBlendingMode
import ru.tech.imageresizershrinker.core.filters.domain.model.RadialTiltShiftParams
import ru.tech.imageresizershrinker.core.filters.domain.model.SideFadeParams
import ru.tech.imageresizershrinker.core.filters.domain.model.TransferFunc
import ru.tech.imageresizershrinker.core.filters.domain.model.WaterParams
import ru.tech.imageresizershrinker.core.filters.domain.model.wrap
import ru.tech.imageresizershrinker.core.filters.presentation.model.UiColorFilter
import ru.tech.imageresizershrinker.core.filters.presentation.model.UiFilter
import ru.tech.imageresizershrinker.core.filters.presentation.model.UiRGBFilter
import ru.tech.imageresizershrinker.core.resources.R
import ru.tech.imageresizershrinker.core.ui.utils.helper.toColor
import ru.tech.imageresizershrinker.core.ui.utils.helper.toFileModel
import ru.tech.imageresizershrinker.core.ui.utils.helper.toImageModel
import ru.tech.imageresizershrinker.core.ui.utils.helper.toModel
import ru.tech.imageresizershrinker.core.ui.widget.enhanced.EnhancedIconButton
import ru.tech.imageresizershrinker.core.ui.widget.buttons.ToggleGroupButton
import ru.tech.imageresizershrinker.core.ui.widget.color_picker.ColorSelectionRow
import ru.tech.imageresizershrinker.core.ui.widget.color_picker.ColorSelectionRowDefaults
import ru.tech.imageresizershrinker.core.ui.widget.enhanced.EnhancedSlider
import ru.tech.imageresizershrinker.core.ui.widget.enhanced.EnhancedSliderItem
import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.ColorRowSelector
import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.FileSelector
import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.ImageSelector
import ru.tech.imageresizershrinker.core.ui.widget.modifier.ContainerShapeDefaults
import ru.tech.imageresizershrinker.core.ui.widget.modifier.container
import ru.tech.imageresizershrinker.core.ui.widget.preferences.PreferenceRowSwitch
import ru.tech.imageresizershrinker.core.ui.widget.text.RoundedTextField
import kotlin.math.absoluteValue

@Composable
internal fun <T> FilterItemContent(
    filter: UiFilter<T>,
    onFilterChange: (value: Any) -> Unit,
    modifier: Modifier = Modifier,
    previewOnly: Boolean = false,
) {
    Column(
        modifier = modifier
    ) {
        when (val value = filter.value) {
            is FilterValueWrapper<*> -> {
                when (val wrapped = (filter.value as FilterValueWrapper<*>).wrapped) {
                    is ColorModel -> {
                        Box(
                            modifier = Modifier.padding(
                                start = 16.dp,
                                end = 16.dp
                            )
                        ) {
                            ColorSelectionRow(
                                value = remember(wrapped) {
                                    wrapped.toColor()
                                },
                                defaultColors = remember(filter) {
                                    derivedStateOf {
                                        ColorSelectionRowDefaults.colorList.map {
                                            if (filter is UiColorFilter) it.copy(0.5f)
                                            else it
                                        }
                                    }
                                }.value,
                                allowAlpha = filter !is UiRGBFilter,
                                allowScroll = !previewOnly,
                                onValueChange = {
                                    onFilterChange(it.toModel().wrap())
                                }
                            )
                        }
                    }
                }
            }

            is FloatArray -> {
                val rows = filter.paramsInfo[0].valueRange.start.toInt().absoluteValue
                var text by rememberSaveable(value) {
                    mutableStateOf(
                        value.let {
                            var string = ""
                            it.forEachIndexed { index, float ->
                                string += "$float, "
                                if (index % rows == (rows - 1)) string += "\n"
                            }
                            string.dropLast(3)
                        }
                    )
                }
                RoundedTextField(
                    enabled = !previewOnly,
                    modifier = Modifier.padding(16.dp),
                    singleLine = false,
                    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                    onValueChange = { text = it },
                    onLoseFocusTransformation = {
                        val matrix = filter.newInstance().value as FloatArray
                        this.trim { it.isWhitespace() }.split(",").mapIndexed { index, num ->
                            num.toFloatOrNull()?.let {
                                matrix[index] = it
                            }
                        }
                        onFilterChange(matrix)
                        this
                    },
                    endIcon = {
                        EnhancedIconButton(
                            onClick = {
                                val matrix = filter.newInstance().value as FloatArray
                                text.trim { it.isWhitespace() }.split(",")
                                    .mapIndexed { index, num ->
                                        num.toFloatOrNull()?.let {
                                            matrix[index] = it
                                        }
                                    }
                                onFilterChange(matrix)
                            }
                        ) {
                            Icon(
                                imageVector = Icons.Rounded.Done,
                                contentDescription = "Done"
                            )
                        }
                    },
                    value = text,
                    label = {
                        Text(stringResource(R.string.float_array_of))
                    }
                )
            }

            is Float -> {
                EnhancedSlider(
                    modifier = Modifier
                        .padding(top = 16.dp, start = 12.dp, end = 12.dp, bottom = 8.dp)
                        .offset(y = (-2).dp),
                    enabled = !previewOnly,
                    value = value,
                    onValueChange = {
                        onFilterChange(it.roundTo(filter.paramsInfo.first().roundTo))
                    },
                    valueRange = filter.paramsInfo.first().valueRange
                )
            }

            is Boolean -> {
                filter.paramsInfo[0].takeIf { it.title != null }
                    ?.let { (title, _, _) ->
                        PreferenceRowSwitch(
                            title = stringResource(id = title!!),
                            checked = value,
                            onClick = {
                                onFilterChange(value)
                            },
                            modifier = Modifier.padding(
                                top = 16.dp,
                                start = 12.dp,
                                end = 12.dp,
                                bottom = 12.dp
                            ),
                            applyHorizontalPadding = false,
                            startContent = {},
                            resultModifier = Modifier.padding(
                                horizontal = 16.dp,
                                vertical = 8.dp
                            )
                        )
                    }
            }

            is Pair<*, *> -> {
                when {
                    value.first is Number && value.second is Number -> {
                        val sliderState1: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        val sliderState2: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.second as Number).toFloat()) }

                        LaunchedEffect(
                            sliderState1.value,
                            sliderState2.value
                        ) {
                            onFilterChange(
                                sliderState1.value to sliderState2.value
                            )
                        }

                        val paramsInfo by remember(filter) {
                            derivedStateOf {
                                filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                                    if (filterParam.title == null) return@mapIndexedNotNull null
                                    when (index) {
                                        0 -> sliderState1
                                        else -> sliderState2
                                    } to filterParam
                                }
                            }
                        }

                        Column(
                            modifier = Modifier.padding(8.dp)
                        ) {
                            paramsInfo.forEach { (state, info) ->
                                val (title, valueRange, roundTo) = info
                                EnhancedSliderItem(
                                    enabled = !previewOnly,
                                    value = state.value,
                                    title = stringResource(title!!),
                                    valueRange = valueRange,
                                    onValueChange = {
                                        state.value = it
                                    },
                                    internalStateTransformation = {
                                        it.roundTo(roundTo)
                                    },
                                    behaveAsContainer = false
                                )
                            }
                        }
                    }

                    value.first is ColorModel && value.second is ColorModel -> {
                        Box(
                            modifier = Modifier.padding(
                                start = 16.dp,
                                top = 16.dp,
                                end = 16.dp
                            )
                        ) {
                            var color1 by remember(value) { mutableStateOf((value.first as ColorModel).toColor()) }
                            var color2 by remember(value) { mutableStateOf((value.second as ColorModel).toColor()) }

                            Column {
                                ColorRowSelector(
                                    title = stringResource(R.string.first_color),
                                    value = color1,
                                    onValueChange = {
                                        color1 = it
                                        onFilterChange(color1.toModel() to color2.toModel())
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                                Spacer(Modifier.height(8.dp))
                                ColorRowSelector(
                                    title = stringResource(R.string.second_color),
                                    value = color2,
                                    onValueChange = {
                                        color2 = it
                                        onFilterChange(color1.toModel() to color2.toModel())
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                            }
                        }
                    }

                    value.first is Float && value.second is ColorModel -> {
                        var sliderState1 by remember { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var color1 by remember(value) { mutableStateOf((value.second as ColorModel).toColor()) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(sliderState1 to color1.toModel())
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        Box(
                            modifier = Modifier.padding(
                                start = 16.dp,
                                end = 16.dp
                            )
                        ) {
                            ColorRowSelector(
                                title = stringResource(filter.paramsInfo[1].title!!),
                                value = color1,
                                onValueChange = {
                                    color1 = it
                                    onFilterChange(sliderState1 to color1.toModel())
                                },
                                allowScroll = !previewOnly,
                                icon = null,
                                defaultColors = ColorSelectionRowDefaults.colorList,
                                titleFontWeight = FontWeight.Normal,
                                contentHorizontalPadding = 0.dp,
                                modifier = Modifier.padding(start = 4.dp)
                            )
                        }
                    }

                    value.first is Float && value.second is ImageModel -> {
                        var sliderState1 by remember { mutableFloatStateOf((value.first as Float).toFloat()) }
                        var uri1 by remember(value) { mutableStateOf((value.second as ImageModel).data) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(sliderState1 to uri1.toImageModel())
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        ImageSelector(
                            modifier = Modifier.padding(16.dp),
                            value = uri1,
                            title = filter.paramsInfo[1].title?.let {
                                stringResource(it)
                            } ?: stringResource(R.string.image),
                            onValueChange = {
                                uri1 = it.toString()
                                onFilterChange(sliderState1 to uri1.toImageModel())
                            },
                            subtitle = null
                        )
                    }

                    value.first is Float && value.second is FileModel -> {
                        var sliderState1 by remember { mutableFloatStateOf((value.first as Float).toFloat()) }
                        var uri1 by remember(value) { mutableStateOf((value.second as FileModel).uri) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(sliderState1 to uri1.toFileModel())
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        FileSelector(
                            modifier = Modifier.padding(16.dp),
                            value = uri1,
                            title = filter.paramsInfo[1].title?.let {
                                stringResource(it)
                            } ?: stringResource(R.string.pick_file),
                            onValueChange = {
                                uri1 = it.toString()
                                onFilterChange(sliderState1 to uri1.toFileModel())
                            },
                            subtitle = null
                        )
                    }

                    value.first is Number && value.second is Boolean -> {
                        var sliderState1 by remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var booleanState2 by remember(value) { mutableStateOf(value.second as Boolean) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(sliderState1 to booleanState2)
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        filter.paramsInfo[1].takeIf { it.title != null }
                            ?.let { (title, _, _) ->
                                PreferenceRowSwitch(
                                    title = stringResource(id = title!!),
                                    checked = booleanState2,
                                    onClick = {
                                        booleanState2 = it
                                        onFilterChange(sliderState1 to it)
                                    },
                                    modifier = Modifier.padding(
                                        top = 16.dp,
                                        start = 12.dp,
                                        end = 12.dp,
                                        bottom = 12.dp
                                    ),
                                    applyHorizontalPadding = false,
                                    startContent = {},
                                    resultModifier = Modifier.padding(
                                        horizontal = 16.dp,
                                        vertical = 8.dp
                                    )
                                )
                            }
                    }

                    value.first is Number && value.second is BlurEdgeMode -> {
                        var sliderState1 by remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var edgeMode by remember(value) { mutableStateOf(value.second as BlurEdgeMode) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(sliderState1 to edgeMode)
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        filter.paramsInfo[1].takeIf {
                            it.title != null
                        }?.let { (title, _, _) ->
                            EdgeModeSelector(
                                title = title,
                                filter = filter,
                                value = edgeMode,
                                onValueChange = {
                                    edgeMode = it
                                    onFilterChange(sliderState1 to edgeMode)
                                }
                            )
                        }
                    }

                    value.first is Number && value.second is TransferFunc -> {
                        var sliderState1 by remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var transferFunction by remember(value) { mutableStateOf(value.second as TransferFunc) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(sliderState1 to transferFunction)
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        filter.paramsInfo[1].takeIf {
                            it.title != null
                        }?.let { (title, _, _) ->
                            TransferFuncSelector(
                                title = title,
                                filter = filter,
                                value = transferFunction,
                                onValueChange = {
                                    transferFunction = it
                                    onFilterChange(sliderState1 to transferFunction)
                                }
                            )
                        }
                    }
                }
            }

            is Triple<*, *, *> -> {
                when {
                    value.first is Float && value.second is PaletteTransferSpace && value.third is ImageModel -> {
                        var sliderState1 by remember { mutableFloatStateOf((value.first as Float).toFloat()) }
                        var colorSpace1 by remember { mutableStateOf((value.second as PaletteTransferSpace)) }
                        var uri1 by remember(value) { mutableStateOf((value.third as ImageModel).data) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(
                                    Triple(
                                        sliderState1,
                                        colorSpace1,
                                        uri1.toImageModel()
                                    )
                                )
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        Spacer(Modifier.height(8.dp))
                        Column(
                            modifier = Modifier
                                .padding(horizontal = 16.dp)
                                .container(
                                    shape = ContainerShapeDefaults.topShape,
                                    color = MaterialTheme.colorScheme.surfaceContainerLow
                                )
                        ) {
                            Text(
                                text = stringResource(filter.paramsInfo[1].title!!),
                                modifier = Modifier.padding(
                                    top = 8.dp,
                                    start = 12.dp,
                                    end = 12.dp,
                                )
                            )
                            val entries by remember(filter) {
                                derivedStateOf {
                                    PaletteTransferSpace.entries
                                }
                            }
                            ToggleGroupButton(
                                inactiveButtonColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                                items = entries.map { it.translatedName },
                                selectedIndex = entries.indexOf(colorSpace1),
                                onIndexChange = {
                                    colorSpace1 = entries[it]
                                    onFilterChange(
                                        Triple(
                                            sliderState1,
                                            colorSpace1,
                                            uri1.toImageModel()
                                        )
                                    )
                                }
                            )
                        }
                        Spacer(Modifier.height(4.dp))
                        ImageSelector(
                            modifier = Modifier.padding(
                                horizontal = 16.dp
                            ),
                            value = uri1,
                            title = filter.paramsInfo[2].title?.let {
                                stringResource(it)
                            } ?: stringResource(R.string.image),
                            onValueChange = {
                                uri1 = it.toString()
                                onFilterChange(
                                    Triple(
                                        sliderState1,
                                        colorSpace1,
                                        uri1.toImageModel()
                                    )
                                )
                            },
                            subtitle = null,
                            shape = ContainerShapeDefaults.bottomShape
                        )
                        Spacer(Modifier.height(16.dp))
                    }

                    value.first is Number && value.second is Number && value.third is Number -> {
                        val sliderState1: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        val sliderState2: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.second as Number).toFloat()) }
                        val sliderState3: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.third as Number).toFloat()) }

                        LaunchedEffect(
                            sliderState1.value,
                            sliderState2.value,
                            sliderState3.value
                        ) {
                            onFilterChange(
                                Triple(
                                    sliderState1.value,
                                    sliderState2.value,
                                    sliderState3.value
                                )
                            )
                        }

                        val paramsInfo by remember(filter) {
                            derivedStateOf {
                                filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                                    if (filterParam.title == null) return@mapIndexedNotNull null
                                    when (index) {
                                        0 -> sliderState1
                                        1 -> sliderState2
                                        else -> sliderState3
                                    } to filterParam
                                }
                            }
                        }

                        Column(
                            modifier = Modifier.padding(8.dp)
                        ) {
                            paramsInfo.forEach { (state, info) ->
                                val (title, valueRange, roundTo) = info
                                EnhancedSliderItem(
                                    enabled = !previewOnly,
                                    value = state.value,
                                    title = stringResource(title!!),
                                    valueRange = valueRange,
                                    onValueChange = {
                                        state.value = it
                                    },
                                    internalStateTransformation = {
                                        it.roundTo(roundTo)
                                    },
                                    behaveAsContainer = false
                                )
                            }
                        }
                    }

                    value.first is Number && value.second is Number && value.third is ColorModel -> {
                        val sliderState1: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        val sliderState2: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.second as Number).toFloat()) }
                        var color3 by remember(value) { mutableStateOf((value.third as ColorModel).toColor()) }

                        LaunchedEffect(
                            sliderState1.value,
                            sliderState2.value,
                            color3
                        ) {
                            onFilterChange(
                                Triple(sliderState1.value, sliderState2.value, color3.toModel())
                            )
                        }

                        val paramsInfo by remember(filter) {
                            derivedStateOf {
                                filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                                    if (filterParam.title == null || index > 1) return@mapIndexedNotNull null
                                    when (index) {
                                        0 -> sliderState1
                                        else -> sliderState2
                                    } to filterParam
                                }
                            }
                        }

                        Column(
                            modifier = Modifier.padding(
                                top = 8.dp,
                                start = 8.dp,
                                end = 8.dp
                            )
                        ) {
                            paramsInfo.forEach { (state, info) ->
                                val (title, valueRange, roundTo) = info
                                EnhancedSliderItem(
                                    enabled = !previewOnly,
                                    value = state.value,
                                    title = stringResource(title!!),
                                    valueRange = valueRange,
                                    onValueChange = {
                                        state.value = it
                                    },
                                    internalStateTransformation = {
                                        it.roundTo(roundTo)
                                    },
                                    behaveAsContainer = false
                                )
                            }
                        }

                        ColorRowSelector(
                            title = stringResource(filter.paramsInfo[2].title!!),
                            value = color3,
                            onValueChange = {
                                color3 = it
                            },
                            allowScroll = !previewOnly,
                            icon = null,
                            defaultColors = ColorSelectionRowDefaults.colorList,
                            titleFontWeight = FontWeight.Normal,
                            contentHorizontalPadding = 16.dp,
                            modifier = Modifier.padding(start = 4.dp)
                        )
                    }

                    value.first is Number && value.second is ColorModel && value.third is ColorModel -> {
                        var sliderState1 by remember { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var color1 by remember(value) { mutableStateOf((value.second as ColorModel).toColor()) }
                        var color2 by remember(value) { mutableStateOf((value.third as ColorModel).toColor()) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(
                                    Triple(
                                        sliderState1,
                                        color1.toModel(),
                                        color2.toModel()
                                    )
                                )
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        Box(
                            modifier = Modifier.padding(
                                start = 16.dp,
                                top = 16.dp,
                                end = 16.dp
                            )
                        ) {
                            Column {
                                ColorRowSelector(
                                    title = stringResource(filter.paramsInfo[1].title!!),
                                    value = color1,
                                    onValueChange = {
                                        color1 = it
                                        onFilterChange(
                                            Triple(
                                                sliderState1,
                                                color1.toModel(),
                                                color2.toModel()
                                            )
                                        )
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                                Spacer(Modifier.height(8.dp))
                                ColorRowSelector(
                                    title = stringResource(filter.paramsInfo[2].title!!),
                                    value = color1,
                                    onValueChange = {
                                        color2 = it
                                        onFilterChange(
                                            Triple(
                                                sliderState1,
                                                color1.toModel(),
                                                color2.toModel()
                                            )
                                        )
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                            }
                        }
                    }

                    value.first is Number && value.second is Number && value.third is BlurEdgeMode -> {
                        val sliderState1: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        val sliderState2: MutableState<Float> =
                            remember(value) { mutableFloatStateOf((value.second as Number).toFloat()) }
                        var edgeMode by remember(value) { mutableStateOf(value.third as BlurEdgeMode) }

                        LaunchedEffect(
                            sliderState1.value,
                            sliderState2.value,
                            edgeMode
                        ) {
                            onFilterChange(
                                Triple(sliderState1.value, sliderState2.value, edgeMode)
                            )
                        }

                        val paramsInfo by remember(filter) {
                            derivedStateOf {
                                filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                                    if (filterParam.title == null || index > 1) return@mapIndexedNotNull null
                                    when (index) {
                                        0 -> sliderState1
                                        else -> sliderState2
                                    } to filterParam
                                }
                            }
                        }

                        Column(
                            modifier = Modifier.padding(8.dp)
                        ) {
                            paramsInfo.forEach { (state, info) ->
                                val (title, valueRange, roundTo) = info
                                EnhancedSliderItem(
                                    enabled = !previewOnly,
                                    value = state.value,
                                    title = stringResource(title!!),
                                    valueRange = valueRange,
                                    onValueChange = {
                                        state.value = it
                                    },
                                    internalStateTransformation = {
                                        it.roundTo(roundTo)
                                    },
                                    behaveAsContainer = false
                                )
                            }
                        }
                        filter.paramsInfo[2].takeIf { it.title != null }?.let { (title, _, _) ->
                            EdgeModeSelector(
                                title = title,
                                filter = filter,
                                value = edgeMode,
                                onValueChange = { edgeMode = it }
                            )
                        }
                    }

                    value.first is Number && value.second is TransferFunc && value.third is BlurEdgeMode -> {
                        var sliderState1 by remember(value) { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var transferFunction by remember(value) { mutableStateOf(value.second as TransferFunc) }
                        var edgeMode by remember(value) { mutableStateOf(value.third as BlurEdgeMode) }

                        LaunchedEffect(
                            sliderState1,
                            transferFunction,
                            edgeMode
                        ) {
                            onFilterChange(
                                Triple(sliderState1, transferFunction, edgeMode)
                            )
                        }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        filter.paramsInfo[1].takeIf {
                            it.title != null
                        }?.let { (title, _, _) ->
                            TransferFuncSelector(
                                title = title,
                                filter = filter,
                                value = transferFunction,
                                onValueChange = { transferFunction = it }
                            )
                        }
                        filter.paramsInfo[2].takeIf { it.title != null }?.let { (title, _, _) ->
                            EdgeModeSelector(
                                title = title,
                                filter = filter,
                                value = edgeMode,
                                onValueChange = { edgeMode = it }
                            )
                        }
                    }

                    value.first is ColorModel && value.second is ColorModel && value.third is ColorModel -> {
                        Box(
                            modifier = Modifier.padding(
                                start = 16.dp,
                                top = 16.dp,
                                end = 16.dp
                            )
                        ) {
                            var color1 by remember(value) { mutableStateOf((value.first as ColorModel).toColor()) }
                            var color2 by remember(value) { mutableStateOf((value.second as ColorModel).toColor()) }
                            var color3 by remember(value) { mutableStateOf((value.third as ColorModel).toColor()) }

                            Column {
                                ColorRowSelector(
                                    title = stringResource(R.string.first_color),
                                    value = color1,
                                    onValueChange = {
                                        color1 = it
                                        onFilterChange(
                                            Triple(
                                                color1.toModel(),
                                                color2.toModel(),
                                                color3.toModel()
                                            )
                                        )
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                                Spacer(Modifier.height(8.dp))
                                ColorRowSelector(
                                    title = stringResource(R.string.second_color),
                                    value = color2,
                                    onValueChange = {
                                        color2 = it
                                        onFilterChange(
                                            Triple(
                                                color1.toModel(),
                                                color2.toModel(),
                                                color3.toModel()
                                            )
                                        )
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                                Spacer(Modifier.height(8.dp))
                                ColorRowSelector(
                                    title = stringResource(R.string.third_color),
                                    value = color3,
                                    onValueChange = {
                                        color3 = it
                                        onFilterChange(
                                            Triple(
                                                color1.toModel(),
                                                color2.toModel(),
                                                color3.toModel()
                                            )
                                        )
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                            }
                        }
                    }

                    value.first is Number && value.second is ColorModel && value.third is PopArtBlendingMode -> {
                        var sliderState1 by remember { mutableFloatStateOf((value.first as Number).toFloat()) }
                        var color1 by remember(value) { mutableStateOf((value.second as ColorModel).toColor()) }
                        var blendMode1 by remember(value) { mutableStateOf((value.third as PopArtBlendingMode)) }

                        EnhancedSliderItem(
                            modifier = Modifier
                                .padding(
                                    top = 8.dp,
                                    start = 8.dp,
                                    end = 8.dp
                                ),
                            enabled = !previewOnly,
                            value = sliderState1,
                            title = filter.paramsInfo[0].title?.let {
                                stringResource(it)
                            } ?: "",
                            onValueChange = {
                                sliderState1 = it
                                onFilterChange(
                                    Triple(
                                        sliderState1,
                                        color1.toModel(),
                                        blendMode1
                                    )
                                )
                            },
                            internalStateTransformation = {
                                it.roundTo(filter.paramsInfo[0].roundTo)
                            },
                            valueRange = filter.paramsInfo[0].valueRange,
                            behaveAsContainer = false
                        )
                        Box(
                            modifier = Modifier.padding(
                                start = 16.dp,
                                top = 16.dp,
                                end = 16.dp
                            )
                        ) {
                            Column {
                                ColorRowSelector(
                                    title = stringResource(filter.paramsInfo[1].title!!),
                                    value = color1,
                                    onValueChange = {
                                        color1 = it
                                        onFilterChange(
                                            Triple(
                                                sliderState1,
                                                color1.toModel(),
                                                blendMode1
                                            )
                                        )
                                    },
                                    allowScroll = !previewOnly,
                                    icon = null,
                                    defaultColors = ColorSelectionRowDefaults.colorList,
                                    titleFontWeight = FontWeight.Normal,
                                    contentHorizontalPadding = 0.dp
                                )
                                Text(
                                    text = stringResource(filter.paramsInfo[2].title!!),
                                    modifier = Modifier.padding(
                                        top = 8.dp,
                                        start = 12.dp,
                                        end = 12.dp,
                                    )
                                )
                                val entries by remember(filter) {
                                    derivedStateOf {
                                        PopArtBlendingMode.entries
                                    }
                                }
                                ToggleGroupButton(
                                    inactiveButtonColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                                    items = entries.map { it.translatedName },
                                    selectedIndex = entries.indexOf(blendMode1),
                                    onIndexChange = {
                                        blendMode1 = entries[it]
                                        onFilterChange(
                                            Triple(
                                                sliderState1,
                                                color1.toModel(),
                                                blendMode1
                                            )
                                        )
                                    }
                                )
                            }
                        }
                    }
                }
            }

            is RadialTiltShiftParams -> {
                val blurRadius: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.blurRadius as Number).toFloat()) }
                val sigma: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.sigma as Number).toFloat()) }
                val anchorX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.anchorX as Number).toFloat()) }
                val anchorY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.anchorY as Number).toFloat()) }
                val holeRadius: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.holeRadius as Number).toFloat()) }

                LaunchedEffect(
                    blurRadius.value,
                    sigma.value,
                    anchorX.value,
                    anchorY.value,
                    holeRadius.value
                ) {
                    onFilterChange(
                        RadialTiltShiftParams(
                            blurRadius = blurRadius.value,
                            sigma = sigma.value,
                            anchorX = anchorX.value,
                            anchorY = anchorY.value,
                            holeRadius = holeRadius.value
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> blurRadius
                                1 -> sigma
                                2 -> anchorX
                                3 -> anchorY
                                else -> holeRadius
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                }
            }

            is LinearTiltShiftParams -> {
                val blurRadius: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.blurRadius as Number).toFloat()) }
                val sigma: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.sigma as Number).toFloat()) }
                val anchorX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.anchorX as Number).toFloat()) }
                val anchorY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.anchorY as Number).toFloat()) }
                val holeRadius: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.holeRadius as Number).toFloat()) }
                val angle: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.angle as Number).toFloat()) }

                LaunchedEffect(
                    blurRadius.value,
                    sigma.value,
                    anchorX.value,
                    anchorY.value,
                    holeRadius.value,
                    angle.value
                ) {
                    onFilterChange(
                        LinearTiltShiftParams(
                            blurRadius = blurRadius.value,
                            sigma = sigma.value,
                            anchorX = anchorX.value,
                            anchorY = anchorY.value,
                            holeRadius = holeRadius.value,
                            angle = angle.value
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> blurRadius
                                1 -> sigma
                                2 -> anchorX
                                3 -> anchorY
                                4 -> holeRadius
                                else -> angle
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                }
            }

            is GlitchParams -> {
                val channelsShiftX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.channelsShiftX as Number).toFloat()) }
                val channelsShiftY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.channelsShiftY as Number).toFloat()) }
                val corruptionSize: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.corruptionSize as Number).toFloat()) }
                val corruptionCount: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.corruptionCount as Number).toFloat()) }
                val corruptionShiftX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.corruptionShiftX as Number).toFloat()) }
                val corruptionShiftY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.corruptionShiftY as Number).toFloat()) }

                LaunchedEffect(
                    channelsShiftX.value,
                    channelsShiftY.value,
                    corruptionSize.value,
                    corruptionCount.value,
                    corruptionShiftX.value,
                    corruptionShiftY.value
                ) {
                    onFilterChange(
                        GlitchParams(
                            channelsShiftX = channelsShiftX.value,
                            channelsShiftY = channelsShiftY.value,
                            corruptionSize = corruptionSize.value,
                            corruptionCount = corruptionCount.value.toInt(),
                            corruptionShiftX = corruptionShiftX.value,
                            corruptionShiftY = corruptionShiftY.value
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> channelsShiftX
                                1 -> channelsShiftY
                                2 -> corruptionSize
                                3 -> corruptionCount
                                4 -> corruptionShiftX
                                else -> corruptionShiftY
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                }
            }

            is SideFadeParams.Relative -> {
                var scale by remember(value) { mutableFloatStateOf(value.scale) }
                var sideFade by remember(value) { mutableStateOf(value.side) }

                LaunchedEffect(
                    scale, sideFade
                ) {
                    onFilterChange(
                        SideFadeParams.Relative(
                            side = sideFade,
                            scale = scale
                        )
                    )
                }

                EnhancedSliderItem(
                    modifier = Modifier
                        .padding(
                            top = 8.dp,
                            start = 8.dp,
                            end = 8.dp
                        ),
                    enabled = !previewOnly,
                    value = scale,
                    title = filter.paramsInfo[0].title?.let {
                        stringResource(it)
                    } ?: "",
                    onValueChange = {
                        scale = it
                    },
                    internalStateTransformation = {
                        it.roundTo(filter.paramsInfo[0].roundTo)
                    },
                    valueRange = filter.paramsInfo[0].valueRange,
                    behaveAsContainer = false
                )
                filter.paramsInfo[1].takeIf { it.title != null }
                    ?.let { (title, _, _) ->
                        Text(
                            text = stringResource(title!!),
                            modifier = Modifier.padding(
                                top = 8.dp,
                                start = 12.dp,
                                end = 12.dp,
                            )
                        )
                        ToggleGroupButton(
                            inactiveButtonColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                            items = FadeSide.entries.map { it.translatedName },
                            selectedIndex = FadeSide.entries.indexOf(sideFade),
                            onIndexChange = {
                                sideFade = FadeSide.entries[it]
                            }
                        )
                    }
            }

            is WaterParams -> {
                val fractionSize: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.fractionSize) }
                val frequencyX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.frequencyX) }
                val frequencyY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.frequencyY) }
                val amplitudeX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.amplitudeX) }
                val amplitudeY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.amplitudeY) }

                LaunchedEffect(
                    fractionSize.value,
                    frequencyX.value,
                    frequencyY.value,
                    amplitudeX.value,
                    amplitudeY.value
                ) {
                    onFilterChange(
                        WaterParams(
                            fractionSize = fractionSize.value,
                            frequencyX = frequencyX.value,
                            frequencyY = frequencyY.value,
                            amplitudeX = amplitudeX.value,
                            amplitudeY = amplitudeY.value
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> fractionSize
                                1 -> frequencyX
                                2 -> frequencyY
                                3 -> amplitudeX
                                else -> amplitudeY
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                }
            }

            is EnhancedZoomBlurParams -> {
                val radius: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.radius as Number).toFloat()) }
                val sigma: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.sigma as Number).toFloat()) }
                val anchorX: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.centerX as Number).toFloat()) }
                val anchorY: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.centerY as Number).toFloat()) }
                val strength: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.strength as Number).toFloat()) }
                val angle: MutableState<Float> =
                    remember(value) { mutableFloatStateOf((value.angle as Number).toFloat()) }

                LaunchedEffect(
                    radius.value,
                    sigma.value,
                    anchorX.value,
                    anchorY.value,
                    strength.value,
                    angle.value
                ) {
                    onFilterChange(
                        EnhancedZoomBlurParams(
                            radius = radius.value.toInt(),
                            sigma = sigma.value,
                            centerX = anchorX.value,
                            centerY = anchorY.value,
                            strength = strength.value,
                            angle = angle.value
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> radius
                                1 -> sigma
                                2 -> anchorX
                                3 -> anchorY
                                4 -> strength
                                else -> angle
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                }
            }

            is ClaheParams -> {
                val threshold: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.threshold) }
                val gridSizeHorizontal: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.gridSizeHorizontal.toFloat()) }
                val gridSizeVertical: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.gridSizeVertical.toFloat()) }
                val binsCount: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.binsCount.toFloat()) }

                LaunchedEffect(
                    threshold.value,
                    gridSizeHorizontal.value,
                    gridSizeVertical.value,
                    binsCount.value
                ) {
                    onFilterChange(
                        ClaheParams(
                            threshold = threshold.value,
                            gridSizeHorizontal = gridSizeHorizontal.value.toInt(),
                            gridSizeVertical = gridSizeVertical.value.toInt(),
                            binsCount = binsCount.value.toInt()
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> threshold
                                1 -> gridSizeHorizontal
                                2 -> gridSizeVertical
                                else -> binsCount
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                }
            }

            is LinearGaussianParams -> {
                val kernelSize: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.kernelSize.toFloat()) }
                val sigma: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.sigma) }
                val edgeMode: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.edgeMode.ordinal.toFloat()) }
                val transferFunction: MutableState<Float> =
                    remember(value) { mutableFloatStateOf(value.transferFunction.ordinal.toFloat()) }

                LaunchedEffect(
                    kernelSize.value,
                    sigma.value,
                    edgeMode.value,
                    transferFunction.value
                ) {
                    onFilterChange(
                        LinearGaussianParams(
                            kernelSize = kernelSize.value.toInt(),
                            sigma = sigma.value,
                            edgeMode = BlurEdgeMode.entries[edgeMode.value.toInt()],
                            transferFunction = TransferFunc.entries[transferFunction.value.toInt()]
                        )
                    )
                }

                val paramsInfo by remember(filter) {
                    derivedStateOf {
                        filter.paramsInfo.mapIndexedNotNull { index, filterParam ->
                            if (filterParam.title == null) return@mapIndexedNotNull null
                            when (index) {
                                0 -> kernelSize
                                1 -> sigma
                                2 -> edgeMode
                                else -> transferFunction
                            } to filterParam
                        }
                    }
                }

                Column(
                    modifier = Modifier.padding(8.dp)
                ) {
                    paramsInfo.take(2).forEach { (state, info) ->
                        val (title, valueRange, roundTo) = info
                        EnhancedSliderItem(
                            enabled = !previewOnly,
                            value = state.value,
                            title = stringResource(title!!),
                            valueRange = valueRange,
                            onValueChange = {
                                state.value = it
                            },
                            internalStateTransformation = {
                                it.roundTo(roundTo)
                            },
                            behaveAsContainer = false
                        )
                    }
                    paramsInfo[2].let { (state, info) ->
                        EdgeModeSelector(
                            title = info.title!!,
                            filter = filter,
                            value = BlurEdgeMode.entries[state.value.toInt()],
                            onValueChange = { state.value = it.ordinal.toFloat() }
                        )
                    }
                    paramsInfo[3].let { (state, info) ->
                        TransferFuncSelector(
                            title = info.title!!,
                            filter = filter,
                            value = TransferFunc.entries[state.value.toInt()],
                            onValueChange = { state.value = it.ordinal.toFloat() }
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun <T> EdgeModeSelector(
    title: Int?,
    filter: UiFilter<T>,
    value: BlurEdgeMode,
    onValueChange: (BlurEdgeMode) -> Unit,
) {
    Text(
        text = stringResource(title!!),
        modifier = Modifier.padding(
            top = 8.dp,
            start = 12.dp,
            end = 12.dp,
        )
    )
    val entries = BlurEdgeMode.entries

    ToggleGroupButton(
        inactiveButtonColor = MaterialTheme.colorScheme.surfaceContainerHigh,
        items = entries.map { it.translatedName },
        selectedIndex = entries.indexOf(value),
        onIndexChange = {
            onValueChange(entries[it])
        }
    )
}

@Composable
private fun <T> TransferFuncSelector(
    title: Int?,
    filter: UiFilter<T>,
    value: TransferFunc,
    onValueChange: (TransferFunc) -> Unit,
) {
    Text(
        text = stringResource(title!!),
        modifier = Modifier.padding(
            top = 8.dp,
            start = 12.dp,
            end = 12.dp,
        )
    )
    val entries by remember(filter) {
        derivedStateOf {
            TransferFunc.entries
        }
    }
    ToggleGroupButton(
        inactiveButtonColor = MaterialTheme.colorScheme.surfaceContainerHigh,
        items = entries.map { it.translatedName },
        selectedIndex = entries.indexOf(value),
        onIndexChange = {
            onValueChange(entries[it])
        }
    )
}

private val PopArtBlendingMode.translatedName: String
    @Composable
    get() = when (this) {
        PopArtBlendingMode.MULTIPLY -> "Multiply"
        PopArtBlendingMode.COLOR_BURN -> "Color Burn"
        PopArtBlendingMode.SOFT_LIGHT -> "Soft Light"
        PopArtBlendingMode.HSL_COLOR -> "HSL Color"
        PopArtBlendingMode.HSL_HUE -> "HSL Hue"
        PopArtBlendingMode.DIFFERENCE -> "Difference"
    }

private val PaletteTransferSpace.translatedName: String
    @Composable
    get() = when (this) {
        PaletteTransferSpace.LALPHABETA -> "Lαβ"
        PaletteTransferSpace.LAB -> "LAB"
        PaletteTransferSpace.OKLAB -> "OKLAB"
        PaletteTransferSpace.LUV -> "LUV"
    }

private val TransferFunc.translatedName: String
    @Composable
    get() = when (this) {
        TransferFunc.SRGB -> "sRGB"
        TransferFunc.REC709 -> "Rec.709"
        TransferFunc.GAMMA2P2 -> "${stringResource(R.string.gamma)} 2.2"
        TransferFunc.GAMMA2P8 -> "${stringResource(R.string.gamma)} 2.8"
    }

private val BlurEdgeMode.translatedName: String
    @Composable
    get() = when (this) {
        BlurEdgeMode.Clamp -> stringResource(R.string.tile_mode_clamp)
        BlurEdgeMode.Reflect101 -> stringResource(R.string.mirror_101)
        BlurEdgeMode.Wrap -> stringResource(R.string.wrap)
        BlurEdgeMode.Reflect -> stringResource(R.string.tile_mode_mirror)
    }

private val FadeSide.translatedName: String
    @Composable
    get() = when (this) {
        FadeSide.Start -> stringResource(R.string.start)
        FadeSide.End -> stringResource(R.string.end)
        FadeSide.Top -> stringResource(R.string.top)
        FadeSide.Bottom -> stringResource(R.string.bottom)
    }